package cmms.session;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.AbstractMap.SimpleEntry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import fab.util.StringUtil;
import fab.web.IPrivilegeModel;
import fab.web.mvc.AbstractAction;
import fab.web.mvc.AbstractUserSession;
import fab.web.mvc.ApplicationContext;
import fab.web.mvc.dao.IDao;
import fab.web.mvc.dao.IRowHandler;
import fab.web.mvc.dao.MapRowHandler;
import fab.web.util.DBException;

/**
 * @author eltnam
 * @date 2011-5-23 下午02:52:46
 * @description 权限预处理
 */
public class PrivilegeManager implements IPrivilegeModel {
	
	private ApplicationContext context;
	
	private int pvIndex = Integer.MIN_VALUE;
	
	//表前缀
	private String prefix = "fab_";
	
	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	/**
	 * role对page的cfg定义 
	 * 			roleId 		pv 	attributeName 			level,value
	 */
	private Map<Long, Map<Integer, Map<String, SimpleEntry<Integer, String>>>> roleActionPVCfgMap 
				= new HashMap<Long, Map<Integer, Map<String, SimpleEntry<Integer, String>>>>();
	
	/**
	 * 用户个别定义url权限 
	 * 			userId 		actionId
	 */
	private Map<Long, Set<Integer>> userActionPVMap = new HashMap<Long, Set<Integer>>();
	
	/**
	 * 用户个别定义page的cfg
	 * 			userId 		url 	attributeName,value
	 */
	private Map<Long, Map<String, Map<String, Object>>> userCfgMap = new HashMap<Long, Map<String, Map<String, Object>>>();
	
	// 页面对象
	final Map<Long, String> showUrisMap = new HashMap<Long, String>(20);

	private String sevletPath;

//	private List<Integer> totalOpUrlIds = null;
	
	/**
	 * 初始化方法，在修改权限相关表后，应该重新调用并让所有当前用户session失效
	 * @param dao
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public long load(IDao dao) throws Exception {
		roleActionPVCfgMap.clear();
		userActionPVMap.clear();
		userCfgMap.clear();
		showUrisMap.clear();
//		if(totalOpUrlIds != null) {
//			totalOpUrlIds.clear();
//		}
		context = ApplicationContext.instance();
		sevletPath = context.getContextPath();
		//全角色访问页面
		final Map<Long, String> defuris = new HashMap<Long, String>(10);
		// 查询所有显示页面
		dao.query(StringUtil.union("SELECT T_0.spId, T_0.spType, T_0.spUrl FROM ",prefix,"system_page T_0 WHERE T_0.spStatus<>0"), new IRowHandler() {
			@Override
			public Object processRow(ResultSet totalUrls) throws Exception {
				String url = totalUrls.getString(3);
				if(StringUtil.notNULL(url) && !totalUrls.wasNull()) {
					if (totalUrls.getByte(2) == (byte) 0) {
						// 缓存通用页面
						defuris.put(totalUrls.getLong(1), sevletPath + url);
					} else {
						if(!url.toLowerCase().startsWith("http")) {
							url = sevletPath + url;
						}
						showUrisMap.put(totalUrls.getLong(1), url);
					}
				}
				return null;
			}
		});

		// 全局operation的url.权限值设置为0
		List<String> totalOpUrls = (List<String>) dao.query(StringUtil.union(
				"SELECT T_0.soUrl FROM ",prefix,"system_operation T_0 WHERE T_0.soType=? AND T_0.soStatus<>0"), new IRowHandler() {
					@Override
					public Object processRow(ResultSet rs) throws Exception {
						return sevletPath + rs.getString(1);
					}
				}, (byte) 0);
		if(totalOpUrls != null) {
			totalOpUrls.remove(null);
			for(String url : totalOpUrls) {
				AbstractAction aa = context.getAction(url);
				if(aa != null) {
					aa.setPrivilegeValue(0);
				}
			}
		}
		// 缓存各个角色能访问的所有页面
		List<Map<String, Object>> ros = (List<Map<String, Object>>) dao.query(StringUtil.union(
				"SELECT T_0.srId, T_0.srLevel, T_2.soPageId, T_2.soUrl, T_2.soType FROM ",prefix,"system_role T_0",
				" LEFT JOIN ",prefix,"system_role_operation T_1 ON T_0.srId = T_1.sroRole",
				" LEFT JOIN ",prefix,"system_operation T_2 ON T_1.sroOperation = T_2.soId WHERE T_2.soStatus<>0 AND T_0.srStatus<>0"), new MapRowHandler());
		if(ros != null) {
			String cfgSql = StringUtil.union("SELECT T_1.spcAttribute,T_1.spcValue FROM ",
					prefix,"system_role_page_config T_0",
					" LEFT JOIN ",prefix,"system_page_config T_1 ON T_0.srpcConfig=T_1.spcId",
					" WHERE T_0.srpcRole=? AND T_1.spcPage=? AND T_1.spcStatus<>0");
			for (Map<String, Object> ro : ros) {
				// 缓存operation的url
				loadRolePageCfg(dao, ro, cfgSql, (Byte)ro.get("srLevel"), defuris, (Long)ro.get("srId"));
			}
		}
		// 缓存各个用户能访问的特殊Url
		List<Map<String, Object>> uos = (List<Map<String, Object>>) dao.query(StringUtil.union(
				"SELECT T_0.suoUser, T_1.soPageId, T_1.soUrl, T_1.soType FROM ",prefix,"system_user_operation T_0 ",
				"LEFT JOIN ",prefix,"system_operation T_1 ON T_0.suoOperation = T_1.soId WHERE T_1.soStatus<>0"), new MapRowHandler());
		if(uos != null) {
			for (Map<String, Object> uo : uos) {
				Long userId = (Long) uo.get("suoUser");
				// 缓存operation所属的user
				Set<Integer> pvMap = userActionPVMap.get(userId);
				if(pvMap == null) {
					pvMap = new HashSet<Integer>();
					userActionPVMap.put(userId, pvMap);
				}
				String operationUrl = (String) uo.get("soUrl");
				//有url地址或者soType为100的
				Byte type = (Byte)uo.get("soType");
				if(StringUtil.notNULL(operationUrl) && (type != null && type != 99)) {
					//如果不是show操作，则缓存操作地址
					AbstractAction aa = context.getAction(sevletPath + operationUrl);
					if(aa != null) {
						if(aa.getPrivilegeValue() == 0) {
							aa.setPrivilegeValue(pvIndex++);
						}
						pvMap.add(aa.getPrivilegeValue());
					}
				}
				// 获取所属页面地址
				Long pageId = (Long) uo.get("soPageId");
				AbstractAction aa = context.getAction(showUrisMap.get(pageId));
				if(aa != null) {
					if(aa.getPrivilegeValue() == 0) {
						aa.setPrivilegeValue(pvIndex++);
					}
					pvMap.add(aa.getPrivilegeValue());
				}
			}
		}
		// 缓存各个用户对页面的特殊cfg
		List<Map<String, Object>> cfgOs = (List<Map<String, Object>>) dao.query(StringUtil.union(
				"SELECT T_0.supcUser,T_1.spcPage,T_1.spcAttribute,T_1.spcValue FROM ",
				prefix,"system_user_page_config T_0",
				" LEFT JOIN ",prefix,"system_page_config T_1 ON T_0.supcConfig=T_1.spcId",
				" WHERE T_1.spcStatus<>0"), new MapRowHandler());
		if(cfgOs != null) {
			for(Map<String, Object> cfgo : cfgOs) {
				Long userId = (Long)cfgo.get("supcUser");
				Map<String, Map<String, Object>> urlCfgMap = userCfgMap.get(userId);
				if(urlCfgMap == null) {
					urlCfgMap = new HashMap<String, Map<String, Object>>();
					userCfgMap.put(userId, urlCfgMap);
				}
				Long pageId = (Long) cfgo.get("spcPage");
				String showUri = showUrisMap.get(pageId);
				
				String attrName = (String)cfgo.get("spcAttribute"),
						status = (String)cfgo.get("spcValue");
				Map<String, Object> cfg = urlCfgMap.get(showUri);
				if(StringUtil.notNULL(attrName) && StringUtil.notNULL(status)) {
					if(cfg == null) {
						cfg = new HashMap<String, Object>();
						urlCfgMap.put(showUri, cfg);
					}
					cfg.put(attrName, status);
				}
			}
		}
		return System.currentTimeMillis();
	}

	//根据角色加载页面控制缓存
	private void loadRolePageCfg(IDao dao, Map<String, Object> op, String cfgSql, int level,Map<Long, String> defuris, Long keyId) {
		Map<Integer, Map<String, SimpleEntry<Integer, String>>> pvCfgMap = roleActionPVCfgMap.get(keyId);
		// 获取所属页面地址
		Long pageId = (Long) op.get("soPageId");
		String showUri = showUrisMap.get(pageId);
		String operationUrl = (String) op.get("soUrl");
		//有url地址或者soType为100的
		Byte type = (Byte)op.get("soType");
		if (StringUtil.notNULL(operationUrl) || (type != null && type == 99)) {
			AbstractAction aa = context.getAction(sevletPath + operationUrl);
			if(aa != null) {
				if(aa.getPrivilegeValue() == 0) {
					aa.setPrivilegeValue(pvIndex++);
				}
				if (pvCfgMap == null) {
					pvCfgMap = new HashMap<Integer, Map<String, SimpleEntry<Integer, String>>>(20);
					if(Integer.MAX_VALUE != level) {
						//默认页面的cfg
						for(Entry<Long, String> entry : defuris.entrySet()) {
							loadPageCfg(dao, cfgSql, level, pvCfgMap, entry.getValue(), keyId, entry.getKey());
						}
					}
				}
				//如果不是show操作，则缓存操作地址
				if(type != 99) {
					pvCfgMap.put(aa.getPrivilegeValue(), null);
				}
			}
		}
		if (showUri != null) {
			// 缓存showUrl对应的权限cfg
			loadPageCfg(dao, cfgSql, level, pvCfgMap, showUri, keyId, pageId);
		}
		roleActionPVCfgMap.put(keyId, pvCfgMap);
	}

	private void loadPageCfg(IDao dao, String cfgSql, int level, Map<Integer, Map<String, SimpleEntry<Integer, String>>> pvCfgMap,
			String url, Object... params) {
		AbstractAction aa = context.getAction(url);
		if(aa != null) {
			if(aa.getPrivilegeValue() == 0) {
				aa.setPrivilegeValue(pvIndex++);
			}
			Map<String, SimpleEntry<Integer, String>> cfgMap = pvCfgMap.get(url);
			if (cfgSql != null && cfgMap == null) {
				ResultSet cfgs = dao.query(cfgSql, params);
				try {
					while (cfgs.next()) {
						if (cfgMap == null) {
							//页面控制
							cfgMap = new HashMap<String, SimpleEntry<Integer, String>>(4);
						}
						cfgMap.put(cfgs.getString(1), new SimpleEntry<Integer, String>(level, cfgs.getString(2)));
					}
				} catch (SQLException e) {
					throw new DBException(e);
				} finally {
					dao.close(cfgs);
				}
			}
			pvCfgMap.put(aa.getPrivilegeValue(), cfgMap);
		}
	}

	@Override
	public void setUserPrivilege(AbstractUserSession us) {
		// 预处理可以访问的uri和对应的cfg，用户的角色对应的权限和对应的菜单页面
		Map<Integer, Map<String, Integer>> pvCfgLevelMap = new HashMap<Integer, Map<String, Integer>>(10);
		Map<Integer, Map<String, Object>> pageCfgsMap = new HashMap<Integer, Map<String, Object>>(10);
		Long userId = us.getUserId();
		//全局url访问，先设置0
		us.addAllowedId(0);
		//缓存用户特殊url
		Set<Integer> actionIds = userActionPVMap.get(userId);
		if(actionIds != null) {
			us.addAllowedIds(actionIds);
		}
		// 所有角色
		for (Long roleId : us.getAllRoles()) {
			// 角色对各个页面的细化控制
			loadCfg(roleActionPVCfgMap.get(roleId), us, pvCfgLevelMap, pageCfgsMap);
		}
		//处理用户页面特殊cfg（覆盖角色的页面cfg）
		Map<String, Map<String, Object>> userCfg = userCfgMap.get(userId);
		if(userCfg != null) {
			for(Entry<String, Map<String, Object>> entry : userCfg.entrySet()) {
				Map<String, Object> cfgs = entry.getValue();
				if(cfgs == null) {
					AbstractAction aa = context.getAction(entry.getKey());
					if(aa != null) {
						pageCfgsMap.put(aa.getPrivilegeValue(), cfgs);
					}
				}/* else {
					cfgs.putAll(entry.getValue());
				}*/
			}
		}
		pvCfgLevelMap.clear();
		pvCfgLevelMap = null;
		
		if (!pageCfgsMap.isEmpty()) {
			us.setPageCfgs(pageCfgsMap);
		} /*else {
			pageCfgsMap = null;
		}*/
	}
	
	private void loadCfg(Map<Integer, Map<String, SimpleEntry<Integer, String>>> pvCfgMap, AbstractUserSession us,
			Map<Integer, Map<String, Integer>> pvCfgLevelMap, Map<Integer, Map<String, Object>> pageCfgsMap) {
		if (pvCfgMap != null) {
			// 循环所有页面的细化控制
			for (Entry<Integer, Map<String, SimpleEntry<Integer, String>>> roleUriCfgEntry : pvCfgMap.entrySet()) {
				// 页面uri
				Integer pv = roleUriCfgEntry.getKey();
				// 先缓存有访问权限的页面uri，如果该uri没有细化控制，则只作为显示
				us.addAllowedId(pv);
				// 显示页面的所有细化控制（操作请求页面，没有细化控制）
				Map<String, SimpleEntry<Integer, String>> roleCfgMap = roleUriCfgEntry.getValue();
				if (roleCfgMap != null && !roleCfgMap.isEmpty()) {
					// 从等级缓存中获取该uri的控制
					Map<String, Integer> cfgLevelMap = pvCfgLevelMap.get(pv);
					// 细化控制的详细信息
					Map<String, Object> cfgStausMap = pageCfgsMap.get(pv);
					// 如果等级控制为空，则创建
					if (cfgLevelMap == null) {
						cfgLevelMap = new HashMap<String, Integer>(roleCfgMap.size());
						pvCfgLevelMap.put(pv, cfgLevelMap);
						cfgStausMap = new HashMap<String, Object>(roleCfgMap.size());
						pageCfgsMap.put(pv, cfgStausMap);
					}
					// 循环所有细化控制，设置到session中的缓存对象中
					for (Entry<String, SimpleEntry<Integer, String>> roleCfgEntry : roleCfgMap.entrySet()) {
						String attributeName = roleCfgEntry.getKey();
						Integer currentLevel = cfgLevelMap.get(attributeName);
						Integer otherCfgLevel = roleCfgEntry.getValue().getKey();
						// 按照等级重新设置细化控制的值(等级越低值越小)
						if (currentLevel == null || currentLevel < otherCfgLevel) {
							cfgLevelMap.put(attributeName, otherCfgLevel);
							cfgStausMap.put(attributeName, roleCfgEntry.getValue().getValue());
						}
					}
				}
			}
		}
	}

}
